#include <stdio.h>
#include <string.h>
#include "icm/icmCpuManager.h"

#define SIM_ATTRS (ICM_ATTR_DEFAULT)

int main(int argc, char **argv) {

    const char *appName = argv[1];

    // initialize OVPsim, enabling verbose mode to get statistics at end of execution
    icmInit(ICM_VERBOSE|ICM_STOP_ON_CTRLC, 0, 0);

    const char *armModel    = icmGetVlnvString(NULL, "arm.ovpworld.org", "processor", "arm", "1.0", "model");
    const char *armSemihost = icmGetVlnvString(NULL, "arm.ovpworld.org", "semihosting", "armNewlib", "1.0", "model");
	
	// create the processor bus
    icmBusP bus = icmNewBus("busMain", 32);

    // create two memory regions mapping all memory except the DMAC registers
    icmMemoryP mem1 = icmNewMemory("mem1", ICM_PRIV_RWX, 0xffffffff);

    // connect memories to the bus
    icmConnectMemoryToBus(bus, "sp", mem1, 0x00000000);
    
	// create a processor instance
    icmProcessorP processor = icmNewProcessor(
        "CPU1",             // CPU name
        "arm7",             // CPU type
        0,                  // CPU cpuId
        0,                  // CPU model flags
        32,                 // address bits
        armModel,          // model file
        "modelAttrs",       // morpher attributes
        SIM_ATTRS,          // attributes
        0,                  // user-defined attributes
        armSemihost,       // semi-hosting file
        "modelAttrs"        // semi-hosting attributes
    );

	// connect the processor instruction and data busses to the bus
    icmConnectProcessorBusses(processor, bus, bus);

	////////////////////////////////////////////////////////////////////////////
    // INTERRUPTER Peripheral
    ////////////////////////////////////////////////////////////////////////////
    // instantiate the peripheral
	icmPseP interrupter = icmNewPSE("interrupter", "peripheral/pse.pse", NULL, NULL, NULL);
	
	/////////////////////////////////////////////////////////////////////////
	// CONNECTIONS
	/////////////////////////////////////////////////////////////////////////

    icmNetP irq = icmNewNet("IRQ");
    icmConnectProcessorNet(processor, irq, "irq", ICM_INPUT);
    icmConnectPSENet(interrupter, irq, "intp", ICM_OUTPUT);
   
	// load the application executable file into processor memory space
    if(!icmLoadProcessorMemory(processor, appName, False, False, True)) {
        return -1;
    }
   
    // run simulation
	icmProcessorP stoppedProcessor = icmSimulatePlatform();
	icmTerminate();
    // was simulation interrupted or did it complete
    if(stoppedProcessor && (icmGetStopReason(stoppedProcessor)==ICM_SR_INTERRUPT)) {
        icmPrintf("*** simulation interrupted\n");
    }

    return 0;
}
